#include "ZipReader.h"
#include <stdio.h>
#include <unistd.h>
#include "Logger.h"

using namespace std;

ZipReader::ZipReader(const char* filePath)
{
    logger.setLevelControl(INFO);
    this->file_ = fopen(filePath, "rb");

}

ZipReader::~ZipReader()
{
    if (this->file_)
        fclose(this->file_);
}

list<FileHeader*> ZipReader::extractFileHeaders()
{
    int offset = 0;

    list<FileHeader*> fileHeaderList;

    if (this->file_)
    {
        while (hasNextFileHeader(offset))
        {
            FileHeader* currentFileHeader = extractFileHeaderFields(offset);
            fileHeaderList.push_back(currentFileHeader);
            offset += currentFileHeader->calculateSize();
        }
    }

    return fileHeaderList;
}

FileHeader* ZipReader::extractFileHeaderFields(int offset)
{
    FileHeader* currentFileHeader = new FileHeader();
    unsigned int fileNameLength;
    unsigned int extraFieldLength;
    unsigned int fileCompressedSize;

    fseek(this->file_, offset, SEEK_SET);

    fread(&currentFileHeader->headerSignature_, sizeof (int), 1, this->file_);

    fread(&currentFileHeader->versionNedded_, sizeof (short), 1, this->file_);
    DEBUG("Version nedded: %u", currentFileHeader->versionNedded_);

    fread(&(currentFileHeader->flag_), sizeof (short), 1, this->file_);
    DEBUG("Flag: %u", currentFileHeader->flag_);

    fread(&(currentFileHeader->compressionMethod_), sizeof (short), 1, this->file_);
    DEBUG("Compression Method: %u", currentFileHeader->compressionMethod_);

    fread(&(currentFileHeader->lastModifiedTime_), sizeof (short), 1, this->file_);
    DEBUG("Last modified time: %u", currentFileHeader->lastModifiedTime_);

    fread(&currentFileHeader->lastModifiedDate_, sizeof (short), 1, this->file_);
    DEBUG("Last modification date: %u", currentFileHeader->lastModifiedDate_);

    fread(&currentFileHeader->crc_, sizeof (int), 1, this->file_);
    DEBUG("CRC-32: %d", currentFileHeader->crc_);

    fread(&currentFileHeader->compressedSize_, sizeof (int), 1, this->file_);
    DEBUG("Compressed size: %d", currentFileHeader->compressedSize_);

    fileCompressedSize = currentFileHeader->compressedSize_;

    fread(&currentFileHeader->unCompressedSize_, sizeof (int), 1, this->file_);
    DEBUG("Uncompressed size: %d", currentFileHeader->unCompressedSize_);

    fread(&currentFileHeader->fileNameLength_, sizeof (short), 1, this->file_);
    DEBUG("File name Length : %u", currentFileHeader->fileNameLength_);

    fileNameLength = currentFileHeader->fileNameLength_;

    fread(&currentFileHeader->extraFieldLength_, sizeof (short), 1, this->file_);
    DEBUG("Extra field length: %u", currentFileHeader->extraFieldLength_);

    extraFieldLength = currentFileHeader->extraFieldLength_;

    currentFileHeader->fileName_ = (char*) malloc(fileNameLength + 1);
    fread(currentFileHeader->fileName_, 1, fileNameLength, this->file_);
    currentFileHeader->fileName_[fileNameLength] = '\0';
    DEBUG("File name : %s", currentFileHeader->fileName_);

    currentFileHeader->extraField_ = (char*) malloc(extraFieldLength);
    fread(currentFileHeader->extraField_, 1, extraFieldLength, this->file_);
    DEBUG("Extra field : %s ", currentFileHeader->extraField_);

    currentFileHeader->fileData_ = (char*) malloc(fileCompressedSize);
    fread(currentFileHeader->fileData_, 1, fileCompressedSize, this->file_);
    DEBUG("Current file content: %s", currentFileHeader->fileData_);

    return currentFileHeader;
}

bool ZipReader::hasNextFileHeader(int offset)
{
    fseek(this->file_, offset, SEEK_SET);

    unsigned int signatureReaded = 0;
    fread(&signatureReaded, sizeof (char), sizeof (int), this->file_);

    DEBUG("Readed Signature %d  :  Expected Signature %d\n", signatureReaded, FILE_HEADER_SIGNATURE);


    return signatureReaded == FILE_HEADER_SIGNATURE;
}

long ZipReader::fileSize(FILE * inputFile)
{
    long fileSizeBytes = 0;
    fseek(inputFile, 0, SEEK_END);
    fileSizeBytes = ftell(inputFile);
    fseek(inputFile, 0, SEEK_SET);

    return fileSizeBytes;
}